package controllers;

import helpers.ConsoleUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import be.objectify.deadbolt.java.actions.Group;
import be.objectify.deadbolt.java.actions.Restrict;

import models.Company;
import models.Project;
import models.Resource;
import models.ResourceType;
import play.data.Form;
import play.mvc.Controller;
import play.mvc.Result;
import services.ConfigurationService;
import services.ProjectService;
import services.SynchronizationService;
import views.html.projects.*;

public class Projects extends Controller {

	private static final SynchronizationService synchService = SynchronizationService.getService();
	private static final ConfigurationService configurationService = ConfigurationService.getService();
	private static final ProjectService projectService = ProjectService.getService();

	@Restrict(@Group(Application.ROLE_USER))
	public static Result listProjects() {
		List<Project> projects = Project.find.all();
		return ok(list.render(projects));
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result addProject() {
		Form<Project> form = new Form<Project>(Project.class);
		return addProject(form);
	}

	private static Result addProject(Form<Project> form) {
		List<Project> projects = Project.find.all();
		List<Resource> configurations = configurationService.getAllConfigs();
		return ok(add.render(form, projects, configurations));
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result editProject(long projectId) {
		Project project = Project.findById(projectId);
		if (project != null) {
			Form<Project> form = Form.form(Project.class).fill(project);
			return editProject(project, form);
		} else {
			return redirect(controllers.routes.Projects.listProjects());
		}
	}

	private static Result editProject(Project project, Form<Project> form) {
		Resource config = project.getDefaultConfiguration();
		long configId = 0;
		String configName = null;
		String configContent = null;
		if (config != null) {
			configId = config.getId();
			configName = config.getName();
			configContent = configurationService.getConfigFileContent(config);
		} else {
			configContent = "";
		}
		form.data().put("default-configuration-id", Long.toString(configId));
		form.data().put("default-configuration-name", configName);
		form.data().put("default-configuration-content", configContent);

		List<Resource> configurations = configurationService.getAllConfigs();
		List<Resource> testSuites = Resource.findResourcesByProject(project, ResourceType.TestSuite);
		return ok(edit.render(project.getId(), form, configurations, testSuites));
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result createProject() {
		Form<Project> form = Form.form(Project.class).bindFromRequest();
		if (!form.hasErrors()) {
			Project project = form.get();
			project.setCompany(ConsoleUtils.getCompany());
			project = projectService.saveProject(project);

			boolean createConfig = true;
			Resource defaultConfig = null;
			long configId = 0;

			String defaultConfigId = Form.form().bindFromRequest().get("default-configuration-selection");
			if (NumberUtils.isNumber(defaultConfigId)) {
				configId = NumberUtils.toLong(defaultConfigId);
				if (configurationService.configExists(configId)) {
					defaultConfig = configurationService.getConfig(configId);
					createConfig = false;
				}
			}

			if (createConfig) {
				String configName = Form.form().bindFromRequest().get("default-configuration-name");
				String configContent = Form.form().bindFromRequest().get("default-configuration-content");
				defaultConfig = projectService.initialDefaultConfiguration(project);
				if (StringUtils.isNotBlank(configName)) {
					defaultConfig.setName(configName);
				}
				defaultConfig = configurationService.saveConfig(project, defaultConfig, configContent);
				configId = defaultConfig.getId();
			}

			project.setDefaultConfiguration(defaultConfig);
			project.save();

			return redirect(controllers.routes.Projects.listProjects());
		} else {
			return addProject(form);
		}
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result updateProject(long projectId) {
		Project project = Project.findById(projectId);
		Form<Project> form = Form.form(Project.class).bindFromRequest();
		if (!form.hasErrors()) {
			Project newProject = form.get();
			project.setName(newProject.getName());
			project.setDescription(newProject.getDescription());
			project = projectService.saveProject(project);

			String defaultConfigId = Form.form().bindFromRequest().get("default-configuration-selection");
			String configContent = Form.form().bindFromRequest().get("default-configuration-content");

			boolean createConfig = true;
			Resource defaultConfig = project.getDefaultConfiguration();

			if (NumberUtils.isNumber(defaultConfigId)) {
				long selectedDefaultConfigId = NumberUtils.toLong(defaultConfigId);
				if (configurationService.configExists(selectedDefaultConfigId)) {
					Resource selectedDefaultConfig = null;
					if (defaultConfig == null || defaultConfig.getId() != selectedDefaultConfigId) {
						selectedDefaultConfig = configurationService.getConfig(selectedDefaultConfigId);
					} else {
						selectedDefaultConfig = defaultConfig;
					}

					defaultConfig = configurationService.saveConfig(project, selectedDefaultConfig, configContent);
					project.setDefaultConfiguration(defaultConfig);
					projectService.saveProject(project);

					createConfig = false;
				}
			}

			if (createConfig) {
				String configName = Form.form().bindFromRequest().get("default-configuration-name");
				defaultConfig = projectService.initialDefaultConfiguration(project);
				if (StringUtils.isNotBlank(configName)) {
					defaultConfig.setName(configName);
				}
				defaultConfig = configurationService.saveConfig(project, defaultConfig, configContent);
				project.setDefaultConfiguration(defaultConfig);
				projectService.saveProject(project);
			}

			return redirect(controllers.routes.Projects.editProject(projectId));
		} else {
			return editProject(project, form);
		}
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result synchronizeProjects() {
		final Company company = ConsoleUtils.getCompany();
		synchService.synchronizeProjects(company);
		return redirect(controllers.routes.Projects.listProjects());
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result showDefaultConfig(long projectId) {
		Resource config = Project.findById(projectId).getDefaultConfiguration();
		if (config != null) {
			return redirect(controllers.routes.Configurations.editConfig(config.getId()));
		} else {
			return redirect(controllers.routes.Projects.listProjects());
		}
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result runTestsWithDefaultConfig(long projectId) {
		Resource config = Project.findById(projectId).getDefaultConfiguration();
		return runTestsWithConfig(projectId, config);
	}

	@Restrict(@Group(Application.ROLE_USER))
	public static Result runTestsWithConfig(long projectId, long configId) {
		Resource config = configurationService.getConfig(configId);
		return runTestsWithConfig(projectId, config);
	}

	private static Result runTestsWithConfig(long projectId, Resource config) {
		Project project = Project.findById(projectId);
		if (project == null || config == null) {
			return redirect(controllers.routes.Projects.listProjects());
		}
		String content = "";
		try {
			URL url = projectService.getTestExecuteURL(project, config);

			URLConnection connection = url.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				content += line + "\n";
			}
			in.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ok(content);
	}
}
